#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include "include/types.h"
#include "include/params.h"
#include "include/operat.h"
#include "include/virtware.h"
#include "include/module.h"
#include "include/libcore.h"
#include "include/vmerr.h"
#define PACKED __attribute__((packed))
#define ELF_BUF_SIZ 0x40000U

int8 elf_buf[ELF_BUF_SIZ];
/*
 * Basic types
 */
typedef uint32 Elf32_Addr;
typedef uint32 Elf32_Off;
typedef uint16 Elf32_Section;
typedef uint16 Elf32_Versym;
typedef uint8 Elf_Byte;
typedef uint16 Elf32_Half;
typedef int32 Elf32_Sword;
typedef uint32 Elf32_Word;
typedef int64 Elf32_Sxword;
typedef uint64 Elf32_Xword;

/*
 * ELF header
 */
#define EI_NIDENT 16
typedef struct {
    uint8 e_ident[EI_NIDENT];
    uint16 e_types;
    uint16 e_machine;
    uint32 e_version;
    Elf32_Addr e_entry;
    Elf32_Off e_phoff;
    Elf32_Off e_shoff;
    uint32 e_flags;
    uint16 e_ehsize;
    uint16 e_phentsize;
    uint16 e_phnum;
    uint16 e_shentsize;
    uint16 e_shnum;
    uint16 e_shstrndx;
} PACKED Elf32_Ehdr;

/*
 * Program header
 */
typedef struct {
    uint32 p_type;
    Elf32_Off p_offset;
    Elf32_Addr p_vaddr;
    Elf32_Addr p_paddr;
    uint32 p_filesz;
    uint32 p_memsz;
    uint32 p_flags;
    uint32 p_align;
} PACKED Elf32_Phdr;

#define PT_NULL 0
#define PT_LOAD 1
#define PT_DYNAMIC 2
#define PT_INTERP 3
#define PT_NOTE 4
#define PT_SHLIB 5
#define PT_PHDR 6

#define PF_NONE 0
#define PF_X 1
#define PF_W 2
#define PF_R 4

static int
ehdr_ok (Elf32_Ehdr * ehdr) {
    if (ehdr->e_ident[0] == 0x7f)
    if (ehdr->e_ident[1] == 'E')
    if (ehdr->e_ident[2] == 'L')
    if (ehdr->e_ident[3] == 'F')
    if (ehdr->e_ident[4] == 1) // 32
    if (ehdr->e_ident[5] == 1) // lsb
    if (ehdr->e_types == 2) // exec
    if (ehdr->e_machine == 243) // riscv
    return 1;
    return 0;
}

status
kl_load_kernel (const char* kernel_path, int8* mem, uint32 max_mem) {
    // TODO:加载后pc指向entry point
    status stat = STAT_SUCCESS;
    int elf_fd, n, i, seg_num;
    uint32 seg_hdr_base, filesz, memsz, virt_base, virt_off, rdnum, rdoff;
    Elf32_Ehdr ehdr_buf;
    Elf32_Phdr phdr_buf;
    stat = 0;
    if ((elf_fd = open (kernel_path, O_RDONLY)) == -1) {
        DBG_LOG (kl_load_kernel, "Unable to open kernel file.");
        stat |= ERR_INVELF;
        goto error;
    }
    #define EHDR_SIZ (sizeof (Elf32_Ehdr))
    // #define PHDR_SIZ (sizeof (Elf32_Phdr))
    #define PHDR_SIZ (ehdr_buf.e_phentsize)
    if ((n = read (elf_fd, &ehdr_buf, EHDR_SIZ)) != EHDR_SIZ) {
        DBG_LOG (kl_load_kernel, "Invalid kernel file (too small).");
        stat |= ERR_INVELF;
        goto error;
    }
    if (!ehdr_ok (&ehdr_buf)) {
        DBG_LOG (kl_load_kernel, "Invalid kernel file (elf header check failed).");
        stat |= ERR_INVELF;
        goto error;
    }
    //seg_base = ehdr_buf.e_phoff + ehdr_buf.e_phnum * PHDR_SIZ;
    seg_hdr_base = ehdr_buf.e_phoff;
    seg_num = ehdr_buf.e_phnum;
    DBG_LOG (kl_load_kernel, "seg_num:%d\n", seg_num);
    for (i = 0; i < seg_num; i++) {
        DBG_LOG (kl_load_kernel, "loading seg %d\n", i);
        if (lseek (elf_fd, seg_hdr_base + i * PHDR_SIZ, SEEK_SET) == -1) {
            DBG_LOG (kl_load_kernel, "Unable to seek.");
            stat |= ERR_INVELF;
            goto error;
        }
        if ((n = read (elf_fd, &phdr_buf, PHDR_SIZ)) != PHDR_SIZ) {
            DBG_LOG (kl_load_kernel, "Invalid kernel file (too small).");
            stat |= ERR_INVELF;
            goto error;
        }
        if (phdr_buf.p_type != PT_LOAD) continue; 
        filesz = phdr_buf.p_filesz; // 要读取的大小
        memsz = phdr_buf.p_memsz; // 映射内存大小
        virt_base = phdr_buf.p_vaddr; // 映射起始地址
        virt_off = virt_base; // 映射偏移
        if (virt_base + memsz >= max_mem) {
            DBG_LOG (kl_load_kernel, "Invalid kernel file (insuffcient memory).");
            stat |= ERR_NOMEM;
            goto error;
        }
        //rdoff = seg_base + phdr_buf.p_offset;
        rdoff = phdr_buf.p_offset; // 读取偏移
        //printf ("rdoff:%d\n", rdoff);
        if (lseek (elf_fd, rdoff, SEEK_SET) == -1) {
            DBG_LOG (kl_load_kernel, "Unable to seek.");
            stat |= ERR_INVELF;
            goto error;
        }
        if (filesz == 0) DBG_LOG (kl_load_kernel, "seg%d : Empty segment.", i);
        memset (mem + virt_off, 0, memsz);
        while (filesz > 0) {
            if (filesz < ELF_BUF_SIZ) {
                rdnum = filesz;
            } else {
                rdnum = ELF_BUF_SIZ;
            }
            if ((n = read (elf_fd, elf_buf, rdnum)) <= 0) {
                DBG_LOG (kl_load_kernel,  "Invalid kernel file");
                stat |= ERR_INVELF;
                goto error;
            }
            //printf ("read %d byte\n", n);
            memcpy (mem + virt_off, elf_buf, n);
           // printf ("seg%d : file:0x%X %d bytes -> 0x%X", i, rdoff,rdnum, virt_off);
            DBG_LOG (kl_load_kernel, "seg%d : file:0x%X %d bytes -> 0x%X", i, rdoff,rdnum, virt_off);
            //printf ("test: %s\n", mem + virt_off + 0x1000);
            //printf ("virt off:0x%X\n", virt_off);
            virt_off += n;
            filesz -= n;
        }
        //printf ("0x3000: %X\n",*((uint32*)(mem+0x3000)));
    }
    // getchar ();
    return stat;
    error:
    return stat;
}